﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Collections;

namespace InputFileGenerator
{
    class Program
    {
        private delegate bool Dwarf_Name_Generator(FileStream fs, String[] args);

        private const String HELP_STR = "Generates input data for dwarfs.\n" +
            "\n" +
            "Usage:\n" +
            "        InputFileGenerator.exe -name value -file value [optional arguments]\n" +
            "\n" +
            "Common options:\n" +
            "        -name <name_of_dwarf>\n" +
            "        -file <input_file>\n" +
            "        -size <size_of_input_dataset>\n" +
            "        ...\n" +
            "\n" +
            "Explanation or options\n" +
            "        name - specify the name of dwarf for which input is generated\n" +
            "        file - specify the file into which input data is written\n" +
            "        size - specify the size of input dataset in kB\n" +
            "        ...\n" +
            "\n" +
            "Examples:\n" +
            "        InputFileGenerator.exe -name StructuredGrid -size 512 -file input.txt\n" +
            "        InputFileGenerator.exe -name UnstructuredGrid -size 512 -file input.txt\n" +
            "        InputFileGenerator.exe -name SpectralMethod -size 512 -file input.txt\n" +
            "        InputFileGenerator.exe -name GraphTraversal -size 512 -file input.txt\n" +
            "        ...\n" +
            "\n" +
            "For further information please read documentation in Doc\\InputFileGenerator.docx or browse the InputFileGenerator implementations in Tools\\InputFileGenerator.";

        private const String UNKNOWN_DWARF_NAME = "Unknown dwarf name specified.";
        private const String INVALID_SIZE = "Invalid size value. Argument requires a numerical value " +
            "indicating the size of input data’s memory footprint.";
        private const String ALREADY_EXIST = "File with the same name already exists.";
        private const String ACCESS_DENIED = "Access to target file denied.";
        private const String INVALID_PATH = "Invalid path for target file.";
        private const String NOT_ENOUGH = "Not enough free space available for input file.";
        private const String EXECUTION_DONE = "Generation finished.";
        private const String NO_MANDATORY_OPTION = "Invalid mandatory option.";
        private const String INVALID_MODE_OPTION = "Invalid mode option.";

        private static Dwarf_Name_Generator GetDelegate(String name)
        {
            switch (name)
            {
                case "structuredgrid": return Dwarf_StructuredGrid_Generator.Dwarf_StructuredGrid_Generator;
                case "unstructuredgrid": return Dwarf_UnstructuredGrid_Generator.Dwarf_UnstructuredGrid_Generator;
                case "densealgebra": return Dwarf_DenseAlgebra_Generator.Dwarf_DenseAlgebra_Generator;
                case "sparsealgebra": return Dwarf_SparseAlgebra_Generator.Dwarf_SparseAlgebra_Generator;
                case "combinationallogic": return Dwarf_CombinationalLogic_Generator.Dwarf_CombinationalLogic_Generator;
                case "branchandbound": return Dwarf_BranchAndBound_Generator.Dwarf_BranchAndBound_Generator;
                case "mapreduce": return Dwarf_MapReduce_Generator.Dwarf_MapReduce_Generator;
                case "finitestatemachine": return Dwarf_FiniteStateMachine_Generator.Dwarf_FiniteStateMachine_Generator;
                case "spectralmethod": return Dwarf_SpectralMethod_Generator.Dwarf_SpectralMethod_Generator;
                case "graphtraversal": return Dwarf_GraphTraversal_Generator.Dwarf_GraphTraversal_Generator;
                case "graphmodels": return Dwarf_GraphModels_Generator.Dwarf_GraphModels_Generator;
                case "dynamicprogramming": return Dwarf_DynamicProgramming_Generator.Dwarf_DynamicProgramming_Generator;
            }
            return null;
        }

        private static String GetDwarfSpecifedHelpMessage(String name)
        {
            switch (name)
            {
                case "structuredgrid": return "\nInputFileGenerator.exe  –name StructuredGrid –size 512 –file input.txt \n";
                case "unstructuredgrid": return "\nInputFileGenerator.exe  –name UnstructuredGrid –size 512 –file input.txt \n";
                case "densealgebra": return "\nInputFileGenerator.exe  –name DenseAlgebra –size 512 –file input.txt \n";
                case "sparsealgebra": return "\nInputFileGenerator.exe  –name SparseAlgebra –size 512 –file input.txt \n";
                case "combinationallogic": return "\nInputFileGenerator.exe  –name CombinationalLogic –size 512 –file input.txt \n";
                case "branchandbound": return "\nInputFileGenerator.exe  –name BranchAndBound –size 512 –file input.txt -dim 500 \n";
                case "mapreduce": return "\nInputFileGenerator.exe  –name MapReduce –size 512 –file input.txt -dictionarypath dictionary.txt \n";
                case "finitestatemachine": return "\nInputFileGenerator.exe  –name FiniteStateMachine –size 512 -patternsize 8 –file input.txt -dictionarypath dictionary.txt \n";
                case "spectralmethod": return "\nInputFileGenerator.exe  –name SpectralMethod –size 512 –file input.txt \n";
                case "graphtraversal": return "\nInputFileGenerator.exe  –name GraphTraversal –size 512 –file input.txt \n";
                case "graphmodels": return "\nInputFileGenerator.exe  –name GraphModels –size 512 –file input.txt -states 2000 -sequence 300 \n";
                case "dynamicprogramming": return "\nInputFileGenerator.exe  –name DynamicProgramming –size 512 –file input.txt \n";
            }
            return "";
        }

        static void Main(string[] args)
        {
            String name = String.Empty;
            String file = String.Empty;
            FileStream fs = null;
            int size = 0;
            bool isHelpNeeded = false;
            bool debug = false;
            bool Silent = false;

            ArrayList otherArgs = new ArrayList();
            try
            {
                for (int count = 0; count < args.Length; count++)
                {
                    if (args[count] == "-silent")
                    {
                        Silent = true;
                    }
                    else if (args[count] == "-help")
                    {
                        isHelpNeeded = true;
                    }
                    else if (args[count] == "-name")
                    {
                        if (args.Length == ++count)
                            throw new Exception(NO_MANDATORY_OPTION);
                        name = args[count].Trim().ToLower();
                    }
                    else if (args[count] == "-file")
                    {
                        if (args.Length == ++count)
                            throw new Exception(NO_MANDATORY_OPTION);
                        file = args[count].Trim().ToLower();
                        try
                        {
                            if (File.Exists(file))
                            {
                                throw new Exception(ALREADY_EXIST);
                            }
                        }
                        catch (IOException ex)
                        {
                            //fix: why catch a specific exception just to throw a generic exception?
                            //throw new IOException(INVALID_PATH, ex);
                            throw new IOException(INVALID_PATH, ex);
                        }
                    }
                    else if (args[count] == "-size")
                    {
                        if (args.Length == ++count)
                            throw new Exception(NO_MANDATORY_OPTION);
                        size = int.Parse(args[count]);
                        if (size <= 0)
                        {
                            throw new Exception(INVALID_SIZE);
                        }
                    }
                    else if (args[count] == "-mode")
                    {
                        if (args.Length == ++count)
                            throw new Exception(INVALID_MODE_OPTION);
                        debug = args[count].Equals("debug");
                    }
                    else
                    {
                        otherArgs.Add(args[count]);
                    }
                }

                if (isHelpNeeded || args.Length == 0)
                {
                    throw new Exception(HELP_STR + "\n" + GetDwarfSpecifedHelpMessage(name));
                }

                else if (name == String.Empty || size == 0 || file == String.Empty)
                {
                    throw new Exception(NO_MANDATORY_OPTION);
                }


                otherArgs.Add(size.ToString());
                Dwarf_Name_Generator del = GetDelegate(name);
                if (del == null)
                {
                    throw new Exception(UNKNOWN_DWARF_NAME);
                }

                try
                {
                    fs = new FileStream(file, FileMode.Create, FileAccess.Write, FileShare.Delete);
                }
                catch (UnauthorizedAccessException ex)
                {
                    //fix: why catch a specific exception just to throw a generic exception?
                    //throw new Exception(ACCESS_DENIED);
                    throw new UnauthorizedAccessException(ACCESS_DENIED, ex);
                }
                catch (IOException ex)
                {
                    //fix: why catch a specific exception just to throw a generic exception?
                    //throw new Exception(INVALID_PATH);
                    throw new IOException(INVALID_PATH, ex);
                }

                try
                {
                    del(fs, (String[])otherArgs.ToArray((String.Empty).GetType()));
                    fs.Close();
                    if (!Silent)
                    {
                        Console.WriteLine(EXECUTION_DONE);
                    }
                }
                catch (IOException ex)
                {
                    //fix: why catch a specific exception just to throw a generic exception?
                    //throw new Exception(NOT_ENOUGH);
                    throw new IOException(NOT_ENOUGH, ex);
                }
            }
            catch (Exception ex)
            {
                if (!Silent)
                {
                    Console.WriteLine(ex.Message);
                    if (debug)
                    {
                        Console.WriteLine(ex.StackTrace);
                    }
                }
                if (fs != null && file != String.Empty)
                {
                    fs.Close();
                    File.Delete(file);
                }
            }
        }
    }
}
